﻿using Levfier.GUI.EditorControls.Exceptions;
using Levfier.GUI.Status;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Levfier.FileIO
{
    /// <summary>
    /// アンプとGUIのファイル
    /// </summary>
    [Serializable]
    public class AmplifierFile
    {

        public AmplifierFile()
        {
            //DLLが読み込めなかった場合のイベントハンドラを追加
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        }

        /// <summary>
        /// ファイルにプラグインステータスを書き込む
        /// </summary>
        /// <param name="filename">ファイル名</param>
        /// <param name="statusCreaters">ステータスを生成できるクラス群</param>
        public void WriteToFile(string fileName, IEnumerable<IStatusCreatable> statusCreaters)
        {
            using (Stream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                this.WriteToStream(stream, statusCreaters);
            }
        }

        /// <summary>
        /// ストリームにプラグインステータスを書き込む
        /// </summary>
        /// <param name="stream">ストリーム</param>
        /// <param name="statusCreaters">ステータスを生成できるクラス群</param>
        public void WriteToStream(Stream stream, IEnumerable<IStatusCreatable> statusCreaters)
        {
            BinaryFormatter formatter = new BinaryFormatter();

            //ステータスを生成させる
            IList<ControlStatus> statuses = new List<ControlStatus>();

            foreach (IStatusCreatable creater in statusCreaters)
            {
                //ステータスを作成する
                ControlStatus status = creater.GetStatus();

                //保存対象か判別する
                if (this.IsIgnore(status))
                {
                    statuses.Add(status);
                }
            }

            //書き込み
            formatter.Serialize(stream, statuses);
        }

        /// <summary>
        /// ファイルから読み込む
        /// </summary>
        /// <param name="fileName">ファイル名</param>
        /// <exception cref="FileReadFailException">ファイルの内容をキャストできなかった場合に発生する</exception>
        /// <exception cref="IOException">ファイルが存在しない場合などに発生する</exception>
        public IEnumerable<ControlStatus> ReadFromFile(string fileName)
        {
            IEnumerable<ControlStatus> ret = null;
            using (Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                ret = this.ReadFromStream(stream);
            }

            return ret;
        }

        /// <summary>
        /// ストリームからプラグインステータスを読み込む
        /// </summary>
        /// <param name="stream">ストリーム</param>
        /// <returns>ステータスのコレクション</returns>
        /// <exception cref="FileReadFailException">ファイルの内容をキャストできなかった場合に発生する</exception>
        public IEnumerable<ControlStatus> ReadFromStream(Stream stream)
        {
            IFormatter formatter = new BinaryFormatter();

            //デシリアライズする
            object obj = formatter.Deserialize(stream);
            IList<ControlStatus> statuses = obj as IList<ControlStatus>;

            if (null == statuses)
            {
                throw new FileReadFailException("ファイルの読み込みに失敗しました。", null);
            }
            return statuses;
        }

        /// <summary>
        /// ステータスが保存対象か否かを判別する
        /// </summary>
        /// <param name="status">ステータス</param>
        /// <returns>true : 保存対象</returns>
        private bool IsIgnore(ControlStatus status)
        {
            bool input = status.Name.Contains("Input");
            bool output = status.Name.Contains("Output");
            return !input && !output;
        }

        /// <summary>
        /// アセンブリの読み込みを行う。
        /// </summary>
        /// <param name="sender">イベント発生源</param>
        /// <param name="args">イベント</param>
        /// <returns>アセンブリ</returns>
        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            String path = Assembly.GetExecutingAssembly().Location.ToString();
            //args.RequestingAssembly;
            return Assembly.LoadFrom(path);
        }
    }
}
